<?php
namespace app\index\controller;

use think\Controller;
use QL\QueryList;
use think\Request;

class Mutithread extends Controller
{
    //
    private $filePath = '';
    private $fileName = '';

    private $redis = null;

    private $finishKey = '';
    private $resultKey = '';

    private $notRec = 0;
    public function __construct(Request $request = null)
    {
        parent::__construct($request);

        $this->filePath = RUNTIME_PATH . "user-data/";

        $this->check_folder($this->filePath);

        // 设置 PHP 参数
        ini_set('memory_limit', '512M');
        set_time_limit(0);
        ini_set('default_socket_timeout', -1);  //不超时

        // 初始化 redis
        $this->redis = new \Redis;
        $this->redis->pconnect(config('redis.host'), config('redis.port'));

        $this->finishKey = 'fetch_comments_finish_key_' . microtime(true) . '-' . rand(0,10000);
        $this->resultKey = 'fetch_comments_result_key_' . microtime(true) . '-' . rand(0,10000);
    }

    public function fetch_comments($url = '',$date = ''){

        $url = $this->_get_url(1);
        $date = '2010-01-01';

        // 验证 URL 是否正确
        if(false === strpos($url,'reviewerType=all_reviews')){
            return ' 链接地址错误 !';
        }

        $reg = '/(pageNumber=)\d+/';

        // 初始化抓取完成的信息
        $this->redis->set($this->finishKey,0);
        $this->redis->expire($this->finishKey,600);

        try{
            $loop = 0;
            $threads = 16;

            do_ajain:

            $childPids = [];
            for($page = 1 + $loop; $page <= $threads + $loop; $page ++){

                $p = $page;
                $url = preg_replace($reg,'${1}' . $p,$url);

                $pid = pcntl_fork();

                if(0 == $pid){
                    // 子线程的代码块
                    $this->_get_comments($url,($p - 1),$date);
                    exit(0);
                }elseif($pid > 0){
                    $childPids[] = $pid;
                }
            }

            while (!empty($childPids)) {    //wait for all children to complete
                foreach ($childPids as $key => $pid) {
                    $status = null;
                    $res = pcntl_waitpid($pid, $status, WNOHANG);

                    if ($res == -1 || $res > 0) {   //if the process has already exited
                        unset($childPids[$key]);
                    }
                }
                //here sleep() should be used, if the script is in production and doing some heavy process
                sleep(1);
            }

            if(1 != $this->redis->get($this->finishKey)){
                $loop += $threads;
                echo "\ntry ajain \n\n";
                goto do_ajain;
            }

            // 保存结果

            // 删除 key
            $this->redis->del($this->finishKey);
            //$this->redis->del($this->resultKey);
        }catch (\Exception $e){
            return $e->getMessage();
        }

        return '';
    }

    private function _get_url($prod){
        $url = '';

        switch($prod){
            case 1:
                $url = "https://www.amazon.com/Transmitter-Otium-Bluetooth-Wireless-Modulator/product-reviews/B01DZKERVY/ref=cm_cr_othr_d_show_all?ie=UTF8&reviewerType=all_reviews&pageNumber=1&sortBy=recent";
        }

        return $url;
    }

    /**
     * 抓取评论信息，保存到内存
     * @param $url
     * @param $page
     * @return bool
     */
    private function _get_comments($url,$page,$date){
        echo "fetching : " . $url . "\n";

        $index = ($page - 1) * 10 - $this->notRec;
        $html = $this->_getSslPage($url);
        $rules = array(
            'comments' => array(' .review','html'),
        );
        $data = QueryList::html($html) ->rules($rules) ->query() ->getData();
        unset($html);

        // 抓取失败
        if(!$data || empty($data[0]['comments'])){
            $this->redis->set($this->finishKey,1);
            return false;
        }

        $parseUrl = parse_url($url);

        foreach($data as $val){
            $subRules = array(
                'title' => array('.review-title','text'),
                'starts' => array('.review-rating > .a-icon-alt','text'),
                'author' => array('.author','text'),
                'author_url' => array('.author','href'),
                'comment' => array('.review-text','text'),
                'comment_url' => array('.review-title','href'),
                'date' => array('.review-date','text'),
            );

            $comment = QueryList::html($val['comments'])->rules($subRules)->query()->getData();
            $tmpData = $comment->all()[0];

            // 超出时间段的则返回
            if(strtotime(str_replace('on','',$tmpData['date'])) < strtotime($date)){
                $this->redis->set($this->finishKey,1);
                return false;
            }

            // 排除 4 星 5 星
            if(strpos($tmpData['starts'],'5.0 out') !== false
                || strpos($tmpData['starts'],'4.0 out') !== false){
                $this->notRec ++;
                continue;
            }

            // 补全 url
            $tmpData['author_url'] = $parseUrl['scheme'] . "://" . $parseUrl['host'] . $tmpData['author_url'];
            $tmpData['comment_url'] = $parseUrl['scheme'] . "://" . $parseUrl['host'] . $tmpData['comment_url'];

            $index ++;
            //array_unshift($tmpData,['page' => $page]);
            //array_unshift($tmpData,['index' => $index]);

            $appendData = [
                'index' => $index,
                'page' => $page,
            ];
            $tmpData = array_merge($appendData,$tmpData);

            print_r($tmpData);
            $this->redis->lpush($this->resultKey,json_encode($tmpData));

            unset($tmpData);
            unset($comment);
        }

        unset($data);

        return true;
    }

    /**
     * 执行网络请求
     * @param $url
     * @return mixed
     */
    private function _getSslPage($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_REFERER, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

    private function check_folder($path)
    {
        if(!is_readable($path))
        {
            is_file($path) or mkdir($path,0755);
        }
    }
}
